package player;

import adventure.commands.skills.Skill;
import adventure.misc.Currency;
import adventure.misc.CurrencyImp;
import adventure.placeable.Fightable;
import adventure.placeable.Item;
import adventure.placeable.ItemImp;
import adventure.placeable.Mobile;
import adventure.vocation.Vocation;
import adventure.vocation.VocationImp;
import colour.Colour;
import colour.ColourUtil;
import java.text.DecimalFormat;
import java.util.ArrayList;

/**
 *
 * @author Michael Hanns
 *
 */
public class PlayerImp implements Player, PlayerEdit, Fightable {

    // Location info
    private int x;
    private int y;
    private int z;
    private int spawnX;
    private int spawnY;
    private int spawnZ;
    //
    // Player info
    private boolean[] adminStatus;
    private int id;
    private String name;
    private String description;
    private String title;
    private Vocation vocation;
    //
    // XP
    private int currentXP;
    private int[] xpToLevel;
    private int level;
    //
    // Stats
    private int maxHealth;
    private int health;
    private int maxSpecial;
    private int special;
    private int attack;
    private int defence;
    private int intelligence;
    private int speed;
    private int luck;
    private boolean immortal;
    //
    // Items and equipment
    private Currency currencyType;
    private int currencyAmount;
    private ArrayList<Item> inventory;
    private Item helm;
    private Item chest;
    private Item weapon;
    private Item offhand;
    private Item boots;
    //
    // Quest log
    private LogBook log;
    //
    // Preferences
    private ArrayList<String> subscriptions;
    private boolean suppressTick;
    private String password;
    private ArrayList<Fightable> inCombatWith;
    private ArrayList<Flag> combatFlags;
    private long lastDeath;
    private boolean recordingSpeedwalk;
    private String speedwalk;
    // Group and followers/following
    private char lastMove = ' ';
    private ArrayList<Player> followers;
    private ArrayList<Player> following;
    private ArrayList<Player> group;
    private boolean sitting;

    public PlayerImp() {
        this.id = 0;
        initialise();
    }

    public PlayerImp(int id) {
        this.id = id;
        initialise();
    }

    private void initialise() {
        this.adminStatus = new boolean[2];
        this.adminStatus[0] = false;
        this.adminStatus[1] = false;
        this.name = "";
        this.vocation = new VocationImp(1);
        this.password = "";
        this.log = new LogBookImp();
        this.subscriptions = new ArrayList<String>();
        this.inventory = new ArrayList<Item>();

        this.description = "You see before you "
                + name + ".";
        this.title = "the Apprentice";

        this.currentXP = 0;
        this.level = 1;
        this.maxHealth = 10;
        this.health = 10;
        this.maxSpecial = 10;
        this.special = 10;
        this.immortal = false;

        setBaseStats();

        this.currencyType = new CurrencyImp();
        this.currencyAmount = 0;
        this.weapon = new ItemImp();
        this.offhand = new ItemImp();
        this.helm = new ItemImp();
        this.chest = new ItemImp();
        this.boots = new ItemImp();

        this.x = 1;
        this.y = 1;
        this.z = 1;

        this.spawnX = x;
        this.spawnY = y;
        this.spawnZ = z;

        this.xpToLevel = new int[101];
        setLevelXP();

        this.inCombatWith = new ArrayList<Fightable>();
        this.combatFlags = new ArrayList<Flag>();

        this.lastDeath = System.currentTimeMillis() - 30000;
        this.suppressTick = false;
        this.recordingSpeedwalk = false;
        this.speedwalk = "";

        this.following = new ArrayList<Player>();
        this.followers = new ArrayList<Player>();
        this.group = new ArrayList<Player>();

        this.sitting = false;
    }

    private void setBaseStats() {
        this.attack = vocation.getBaseAttack();
        this.defence = vocation.getBaseDefence();
        this.intelligence = vocation.getBaseIntel();
        this.speed = vocation.getBaseSpeed();
        this.luck = vocation.getBaseLuck();
    }

    private void setLevelXP() {
        xpToLevel[0] = 0;
        xpToLevel[1] = 0;
        xpToLevel[2] = 25;
        for (int c = 3; c < xpToLevel.length; c++) {
            xpToLevel[c] = xpToLevel[c - 1] + (int) (xpToLevel[c - 1] * 1.25);
        }
    }

    @Override
    public void setCurrencyType(Currency type) {
        this.currencyType = type;
    }

    @Override
    public boolean isAdmin() {
        if (adminStatus[0]) {
            return true;
        }
        return false;
    }

    @Override
    public boolean isSuperAdmin() {
        if (adminStatus[0] && adminStatus[1]) {
            return true;
        }
        return false;
    }

    @Override
    public int getID() {
        return id;
    }

    @Override
    public String getName() {
        if (!isDead()) {
            return name;
        } else {
            return "corpse of " + name;
        }
    }

    @Override
    public String getAliveName() {
        return name;
    }

    @Override
    public String getVocationName() {
        return vocation.getName();
    }

    @Override
    public Vocation getVocation() {
        return vocation;
    }

    @Override
    public String getHealthStatus() {
        if (health == maxHealth) {
            return name + " appears to be in perfect health.";
        } else if (health > maxHealth * 0.9) {
            return name + " has a couple of scratches.";
        } else if (health > maxHealth * 0.8) {
            return name + " is looking worse for the wear.";
        } else if (health > maxHealth * 0.6) {
            return name + " is bleeding heavily.";
        } else if (health > maxHealth * 0.4) {
            return name + " is mortally wounded!";
        } else if (health > maxHealth * 0.2) {
            return name + " is in crippling pain!";
        } else {
            return name + " feels the cold touch of death!";
        }
    }

    @Override
    public String getDescription() {
        if (!isDead()) {
            return description.replace(name, ColourUtil.colourise(name, ColourUtil.player));
        } else {
            String desc = "It's the rotting remains of what was once " + ColourUtil.colourise(name, ColourUtil.player) + ".";
            if (inventory.size() > 0) {
                desc = desc.concat("\nContains:");
                for (int c = 0; c < inventory.size(); c++) {
                    desc = desc.concat("\n".concat(ColourUtil.colourise(inventory.get(c).getName(), ColourUtil.item)));
                }
                if (currencyAmount > 1) {
                    desc = desc.concat("\n" + ColourUtil.colourise(currencyAmount + " " + currencyType.getPlural(), ColourUtil.currency));
                } else if (currencyAmount > 0) {
                    desc = desc.concat("\n" + ColourUtil.colourise("a " + currencyType.getSingular(), ColourUtil.currency));
                }
            } else if (currencyAmount > 0) {
                if (currencyAmount > 1) {
                    desc = desc.concat("\nContains:\n" + ColourUtil.colourise(currencyAmount + " " + currencyType.getPlural(), ColourUtil.currency));
                } else {
                    desc = desc.concat("\nContains:\n" + ColourUtil.colourise("a " + currencyType.getSingular(), ColourUtil.currency));
                }

            }
            return desc;
        }
    }

    @Override
    public String getAliveDescription() {
        return description;
    }

    public String getTitle() {
        return title;
    }

    @Override
    public int getHealth() {
        return health;
    }

    @Override
    public int getMaxHealth() {
        return maxHealth;
    }

    @Override
    public int getSpecial() {
        return special;
    }

    @Override
    public int getMaxSpecial() {
        return maxSpecial;
    }

    @Override
    public boolean isImmortal() {
        return immortal;
    }

    @Override
    public String getStatusPrompt() {
        String prompt = "<" + health + "/" + maxHealth + "hp, "
                + special + "/"
                + maxSpecial + "sp, " + (xpToLevel[level + 1] - currentXP) + "tnl>\n\n";

        return prompt;
    }

    @Override
    public String getStatusTable() {
        /* Print the following:
         +-------------------------------------------------------------+
         | Name: HeroesName               Vocation: HeroesVocation     |
         +------------------------------+------------------------------+
         | Level         : <level>      | Attack        : <level>      |
         | Current XP    :              | Defence       : <level>      |
         | XP to level X :              | Speed         : <level>      |
         | Need to level :              | Luck          : <level>      |
         +------------------------------+------------------------------+
         | Weapon        :              | Evade %       :              |
         | Offhand       :              | Crtitical %   :              |
         | Head          :              |                              |
         | Chest         :              | Damage Mod    :              |
         | Feet          :              | Damage Reduct :              |
         +------------------------------+------------------------------+
         | Hit Points    : [    /    ]    Special       : [    /    ]  |
         | <CurrencyID>  : [         ]                                 |
         +-------------------------------------------------------------+
         */
        // Generate the table
        String divider = ColourUtil.colourise(
                " +-------------------------------------------------------------+",
                ColourUtil.grey);
        String divider2 = ColourUtil.colourise(" +------------------------------+------------------------------+",
                ColourUtil.grey);

        String weapstr = weapon.getName();
        if (weapon.getID() == 0) {
            weapstr = "Unarmed";
        }
        String offhandstr = offhand.getName();
        if (offhand.getID() == 0) {
            offhandstr = "Unarmed";
        }
        String cheststr = chest.getName();
        if (chest.getID() == 0) {
            cheststr = "Unarmoured";
        }
        String bootsstr = boots.getName();
        if (boots.getID() == 0) {
            bootsstr = "Unarmoured";
        }
        String helmstr = helm.getName();
        if (helm.getID() == 0) {
            helmstr = "Unarmoured";
        }

        DecimalFormat twoDForm = new DecimalFormat("#.##");
        String evadeChance = Double.valueOf(twoDForm.format(((double) speed - ((double) boots.getModifier() / 4) * (1 + ((double) luck / 100))) / 2)) + "";
        String critChance = Double.valueOf(twoDForm.format(((double) attack * (1 + ((double) luck / 100))) / 2)) + "";
        String dMod = (attack + weapon.getModifier()) + "";
        String dRed = (defence + chest.getModifier() + boots.getModifier()) + "";

        String hpstr = "[";
        for (int c = 0; c < 4 - (health + "").length(); c++) {
            hpstr = hpstr + " ";
        }
        hpstr = hpstr + health + "/" + maxHealth;
        for (int c = 0; c < 4 - (maxHealth + "").length(); c++) {
            hpstr = hpstr + " ";
        }
        hpstr = hpstr + "]";

        String spstr = "[";
        for (int c = 0; c < 4 - (special + "").length(); c++) {
            spstr = spstr + " ";
        }
        spstr = spstr + special + "/" + maxSpecial;
        for (int c = 0; c < 4 - (maxSpecial + "").length(); c++) {
            spstr = spstr + " ";
        }
        spstr = spstr + "]";


        // Fill an array with table details
        String[] table = new String[18];
        table[0] = divider;
        table[1] = fillStatLine("Name", name, "Vocation", vocation.getName(), false);
        System.out.println("Status table - get vocation name: " + vocation.getName());
        table[2] = divider2;
        table[3] = fillStatLine("Level", level + "", "Attack", attack + "", true);
        table[4] = fillStatLine("Current XP", currentXP + "", "Defence", defence + "", true);
        table[5] = fillStatLine(("XP To Lvl " + (level + 1)), xpToLevel[level + 1] + "", "Intelligence", intelligence + "", true);
        table[6] = fillStatLine("Remaining tnl", (xpToLevel[level + 1] - currentXP) + "", "Speed", speed + "", true);
        table[7] = fillStatLine("", "", "Luck", luck + "", true);
        table[8] = divider2;
        table[9] = fillStatLine("Weapon", weapstr, "Evade %", evadeChance, true);
        table[10] = fillStatLine("Offhand", offhandstr, "Critical %", critChance, true);
        table[11] = fillStatLine("Head", helmstr, true);
        table[12] = fillStatLine("Chest", cheststr, "Damage Mod", dMod, true);
        table[13] = fillStatLine("Foot", bootsstr, "Damage Reduct", dRed, true);
        table[14] = divider2;
        table[15] = fillStatLine("Hit points", hpstr, "Special", spstr, false);
        table[16] = fillStatLine(currencyType.getStatusID(), currencyAmount + "", false);
        table[17] = divider;


        // Print the table
        String statusTable = "\n";
        for (int c = 0; c < table.length; c++) {
            statusTable = statusTable + table[c] + "\n";
        }
        return statusTable.concat(ColourUtil.colourise("", ColourUtil.grey));
    }

    private String fillStatLine(String stat, String value, boolean divider) {
        // Create whitespace after stat name
        String statSpace = "";
        for (int c = 0; c < (14 - stat.length()); c++) {
            statSpace = statSpace.concat(" ");
        }

        String whiteSpace = "";

        // Shorten value if too long to fit
        if (value.length() >= 44) {
            value = value.substring(0, 42).concat("..");
        }

        // Create whitespace after value
        if (divider) {
            whiteSpace = ColourUtil.colourise("   |", ColourUtil.grey);
            for (int c = 0; c < (40 - value.length()); c++) {
                whiteSpace = whiteSpace.concat(" ");
            }
        } else {
            for (int c = 0; c < (44 - value.length()); c++) {
                whiteSpace = whiteSpace.concat(" ");
            }
        }
        String line = ColourUtil.colourise(" | ", ColourUtil.grey).concat(
                ColourUtil.colourise(stat, ColourUtil.green).concat(
                statSpace.concat(": ".concat(ColourUtil.colourise(value, ColourUtil.yellow).concat(
                whiteSpace.concat(ColourUtil.colourise("|", ColourUtil.grey)))))));

        return line;
    }

    private String fillStatLine(String stat1, String val1,
            String stat2, String val2, boolean divider) {
        String statSpace = "";
        for (int c = 0; c < (14 - stat1.length()); c++) {
            statSpace = statSpace.concat(" ");
        }

        if (val1.length() >= 13) {
            val1 = val1.substring(0, 11).concat("..");
        }
        String whiteSpace = "";
        for (int c = 0; c < (13 - val1.length()); c++) {
            whiteSpace = whiteSpace.concat(" ");
        }

        String line = ColourUtil.colourise(" | ", ColourUtil.grey).concat(
                ColourUtil.colourise(stat1, ColourUtil.green).concat(
                statSpace.concat(": ".concat(
                ColourUtil.colourise(val1, ColourUtil.yellow).concat(whiteSpace)))));

        if (stat1.length() == 0) {
            line = ColourUtil.colourise(" | ", ColourUtil.grey).concat(
                    statSpace.concat("  ".concat(whiteSpace)));
        }

        if (divider) {
            line = line.concat(ColourUtil.colourise("|", ColourUtil.grey));
        } else {
            line = line.concat(" ");
        }

        statSpace = "";
        for (int c = 0; c < (14 - stat2.length()); c++) {
            statSpace = statSpace.concat(" ");
        }

        if (val2.length() >= 13) {
            val2 = val2.substring(0, 11).concat("..");
        }
        whiteSpace = "";
        for (int c = 0; c < (13 - val2.length()); c++) {
            whiteSpace = whiteSpace.concat(" ");
        }

        if (stat2.length() > 0) {
            line = line.concat(" ".concat(ColourUtil.colourise(stat2, ColourUtil.green).concat(
                    statSpace.concat(": ".concat(ColourUtil.colourise(val2, ColourUtil.yellow).concat(
                    whiteSpace.concat(ColourUtil.colourise("|", ColourUtil.grey))))))));
        } else {
            line = line.concat(" ".concat(statSpace.concat("  ".concat(
                    whiteSpace.concat(ColourUtil.colourise("|", ColourUtil.grey))))));
        }

        return line;
    }

    @Override
    public String getInventoryPrompt() {
        String prompt = ColourUtil.colourise("Inventory:", ColourUtil.white)
                + ColourUtil.colourise("\n----------", ColourUtil.yellow)
                + ColourUtil.colourise("", ColourUtil.grey);

        int maxLength = 0;
        for (int c = 0; c < inventory.size(); c++) {
            if (inventory.get(c).getName().length() > maxLength) {
                maxLength = inventory.get(c).getName().length();
            }
        }
        for (int c = 0; c < inventory.size(); c++) {
            String namePlusWhiteSpace = inventory.get(c).getName();

            while (namePlusWhiteSpace.length() < maxLength) {
                namePlusWhiteSpace += " ";
            }

            namePlusWhiteSpace = ColourUtil.colourise(namePlusWhiteSpace, ColourUtil.item);

            prompt = prompt.concat("\n".concat(namePlusWhiteSpace.concat(" : ".concat(
                    inventory.get(c).getItemType()))));

            if (inventory.get(c).isConsumable()) {
                prompt = prompt
                        + " (remaining uses: "
                        + inventory.get(c).getUsesLeft()
                        + "/"
                        + inventory.get(c).getTotalUses()
                        + ")";
            }
        }
        prompt = prompt + "\n";

        return prompt;
    }

    @Override
    public String getSkillsPrompt() {
        String[] skills = vocation.getSkillsKnown(level);

        String prompt = ColourUtil.colourise("Skills learned:", ColourUtil.white)
                + ColourUtil.colourise("\n----------", ColourUtil.yellow)
                + ColourUtil.colourise("", ColourUtil.grey);

        for (int c = 0; c < skills.length; c++) {
            prompt += "\n" + skills[c];
        }

        return prompt + "\n";
    }

    @Override
    public String[] getItemDescription(String object) {
        boolean found = false;

        // Find exact match
        for (int c = 0; c < inventory.size() && !found; c++) {
            if (inventory.get(c).getName().equalsIgnoreCase(object)) {
                return new String[]{
                            inventory.get(c).getName(),
                            inventory.get(c).getDescription()
                        };
            }
        }

        if (weapon.getName().equalsIgnoreCase(object)) {
            return new String[]{
                        weapon.getName(),
                        weapon.getDescription()
                    };
        } else if (offhand.getName().equalsIgnoreCase(object)) {
            return new String[]{
                        offhand.getName(),
                        offhand.getDescription()
                    };
        } else if (helm.getName().equalsIgnoreCase(object)) {
            return new String[]{
                        helm.getName(),
                        helm.getDescription()
                    };
        } else if (chest.getName().equalsIgnoreCase(object)) {
            return new String[]{
                        chest.getName(),
                        chest.getDescription()
                    };
        } else if (boots.getName().equalsIgnoreCase(object)) {
            return new String[]{
                        boots.getName(),
                        boots.getDescription()
                    };
        }

        // Find starts-with match
        for (int c = 0; c < inventory.size() && !found; c++) {
            if (inventory.get(c).getName().toUpperCase().startsWith(object.toUpperCase())) {
                return new String[]{
                            inventory.get(c).getName(),
                            inventory.get(c).getDescription()
                        };
            }
        }

        if (weapon.getName().toUpperCase().startsWith(object.toUpperCase())) {
            return new String[]{
                        weapon.getName(),
                        weapon.getDescription()
                    };
        } else if (offhand.getName().toUpperCase().startsWith(object.toUpperCase())) {
            return new String[]{
                        offhand.getName(),
                        offhand.getDescription()
                    };
        } else if (helm.getName().toUpperCase().startsWith(object.toUpperCase())) {
            return new String[]{
                        helm.getName(),
                        helm.getDescription()
                    };
        } else if (chest.getName().toUpperCase().startsWith(object.toUpperCase())) {
            return new String[]{
                        chest.getName(),
                        chest.getDescription()
                    };
        } else if (boots.getName().toUpperCase().startsWith(object.toUpperCase())) {
            return new String[]{
                        boots.getName(),
                        boots.getDescription()
                    };
        }

        return null;
    }

    @Override
    public Item[] getAllItems() {
        Item[] items = new Item[inventory.size() + 5];
        for (int c = 0; c < inventory.size(); c++) {
            items[c] = inventory.get(c);
        }

        items[inventory.size()] = weapon;
        items[inventory.size() + 1] = offhand;
        items[inventory.size() + 2] = helm;
        items[inventory.size() + 3] = chest;
        items[inventory.size() + 4] = boots;

        return items;
    }

    @Override
    public int x() {
        return x;
    }

    @Override
    public int y() {
        return y;
    }

    @Override
    public int z() {
        return z;
    }

    @Override
    public int sx() {
        return spawnX;
    }

    @Override
    public int sy() {
        return spawnY;
    }

    @Override
    public int sz() {
        return spawnZ;
    }

    @Override
    public boolean isPlayer() {
        return true;
    }

    @Override
    public int getLevel() {
        return level;
    }

    @Override
    public int getXP() {
        return currentXP;
    }

    @Override
    public int getCurrencyAmount() {
        return currencyAmount;
    }

    @Override
    public int getAttack() {
        return attack + weapon.getModifier();
    }

    @Override
    public int getDefence() {
        return defence;
    }

    @Override
    public int getIntelligence() {
        return intelligence;
    }

    @Override
    public int getSpeed() {
        return speed;
    }

    @Override
    public int getLuck() {
        return luck;
    }

    @Override
    public int getWeaponMod() {
        return weapon.getModifier();
    }

    @Override
    public int getOffhandMod() {
        return offhand.getModifier();
    }

    @Override
    public int getHelmMod() {
        return helm.getModifier();
    }

    @Override
    public int getChestMod() {
        return chest.getModifier();
    }

    @Override
    public int getBootsMod() {
        return boots.getModifier();
    }

    @Override
    public String decayTempItems() {
        String output = "";
        String decayText = "";
        for (int c = 0; c < inventory.size(); c++) {
            decayText = inventory.get(c).decay();

            if (decayText.length() > 0) {
                output += decayText + "\n";
                inventory.remove(c);
                c = 0;
            }
        }

        return output;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public void setAdmin(boolean admin) {
        adminStatus[0] = admin;
        adminStatus[1] = false;

        setImmortal(admin);
    }

    @Override
    public void setSuperAdmin(boolean admin) {
        if (admin) {
            adminStatus[0] = true;
        }
        adminStatus[1] = admin;

        setImmortal(admin);
    }

    @Override
    public void setImmortal(boolean immortal) {
        this.immortal = immortal;
    }

    @Override
    public void setX(int x) {
        this.x = x;
    }

    @Override
    public void setY(int y) {
        this.y = y;
    }

    @Override
    public void setZ(int z) {
        this.z = z;
    }

    @Override
    public void setSpawnX(int x) {
        this.spawnX = x;
    }

    @Override
    public void setSpawnY(int y) {
        this.spawnY = y;
    }

    @Override
    public void setSpawnZ(int z) {
        this.spawnZ = z;
    }

    @Override
    public void setName(String name) {
        if (this.name.length() == 0) {
            this.name = name;
            setDescription("You see before you " + name + ".");
        } else {
            this.name = name;
        }
    }

    @Override
    public void setDescription(String desc) {
        this.description = desc;
    }

    @Override
    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public void setVocation(Vocation voc) {
        this.vocation = voc;
        vocation.getSkillInfo(name);
        setBaseStats();
    }

    @Override
    public void setPassword(String pass) {
        this.password = pass;
    }

    @Override
    public void setMaxHealth(int hp) {
        this.maxHealth = hp;
    }

    @Override
    public void setHealth(int hp) {
        this.health = hp;
    }

    @Override
    public void modifyHealth(int mod) {
        this.health = this.health + mod;
        if (health > maxHealth) {
            this.health = this.maxHealth;
        }
    }

    @Override
    public void setMaxSpecial(int sp) {
        this.maxSpecial = sp;
    }

    @Override
    public void setSpecial(int sp) {
        this.special = sp;
    }

    @Override
    public void setLevel(int lvl) {
        level = lvl;
    }

    @Override
    public void setExperience(int xp) {
        this.currentXP = xp;
    }

    @Override
    public void setCurrencyAmount(int amount) {
        this.currencyAmount = amount;
    }

    @Override
    public void setAttack(int atk) {
        attack = atk;
    }

    @Override
    public void setDefence(int def) {
        defence = def;
    }

    @Override
    public void setIntelligence(int intel) {
        intelligence = intel;
    }

    @Override
    public void setSpeed(int spd) {
        speed = spd;
    }

    @Override
    public void setLuck(int lck) {
        luck = lck;
    }

    @Override
    public boolean isDead() {
        if (health <= 0) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean recordingSpeedwalk() {
        return recordingSpeedwalk;
    }

    @Override
    public String getSpeedwalk() {
        return "run " + speedwalk;
    }

    @Override
    public void recordSpeedwalk(boolean val) {
        this.recordingSpeedwalk = val;
        if (val) {
            clearSpeedwalk();
        }
    }

    @Override
    public void clearSpeedwalk() {
        speedwalk = "";
    }

    @Override
    public void addToSpeedwalk(char dir) {
        speedwalk += dir;
    }

    @Override
    public void move(char dir) {
        switch (dir) {
            case 'N':
                y++;
                break;
            case 'E':
                x++;
                break;
            case 'S':
                y--;
                break;
            case 'W':
                x--;
                break;
            case 'U':
                z++;
                break;
            case 'D':
                z--;
                break;
        }
        this.lastMove = dir;
    }

    private boolean sameRoom(Player p) {
        if (x == p.x() && y == p.y() && z == p.z()) {
            return true;
        }
        return false;
    }

    @Override
    public boolean hasItem(int id, int quantity) {
        int total = 0;
        for (int c = 0; c < inventory.size(); c++) {
            if (inventory.get(c).getID() == id) {
                total++;
                if (total >= quantity) {
                    return true;
                }
            }
        }
        if (weapon.getID() == id) {
            total++;
        } else if (offhand.getID() == id) {
            total++;
        } else if (helm.getID() == id) {
            total++;
        } else if (chest.getID() == id) {
            total++;
        } else if (boots.getID() == id) {
            total++;
        }


        if (total >= quantity) {
            return true;
        }
        return false;
    }

    @Override
    public boolean hasItemUses(int id, int quantity) {
        int total = 0;
        for (int c = 0; c < inventory.size(); c++) {
            if (inventory.get(c).getID() == id) {
                if (!inventory.get(c).isConsumable()) {
                    return true;
                } else {
                    total += inventory.get(c).getUsesLeft();
                }

                if (total >= quantity) {
                    return true;
                }
            }
        }
        if (weapon.getID() == id) {
            if (weapon.isConsumable()) {
                return true;
            } else {
                total += weapon.getUsesLeft();
            }
        } else if (offhand.getID() == id) {
            if (offhand.isConsumable()) {
                return true;
            } else {
                total += offhand.getUsesLeft();
            }
        } else if (helm.getID() == id) {
            if (helm.isConsumable()) {
                return true;
            } else {
                total += helm.getUsesLeft();
            }
        } else if (chest.getID() == id) {
            if (chest.isConsumable()) {
                return true;
            } else {
                total += chest.getUsesLeft();
            }
        } else if (boots.getID() == id) {
            if (boots.isConsumable()) {
                return true;
            } else {
                total += boots.getUsesLeft();
            }
        }


        if (total >= quantity) {
            return true;
        }
        return false;
    }

    @Override
    public boolean hasCurrencyAmount(int val) {
        if (currencyAmount >= val) {
            return true;
        }
        return false;
    }

    @Override
    public void modifyCurrencyAmount(int val) {
        currencyAmount += val;
    }

    @Override
    public Item getKey(int x, int y, int z, char dir) {
        for (int c = 0; c < inventory.size(); c++) {
            if (inventory.get(c).unlocks(x, y, z, dir)) {
                return inventory.get(c);
            }
        }
        return new ItemImp();
    }

    @Override
    public String gainXP(int xp, boolean bonus) {
        this.currentXP = this.currentXP + xp;

        String output = ColourUtil.colourise("You have gained ", ColourUtil.white)
                + ColourUtil.colourise(xp + "", ColourUtil.yellow);
        if (bonus) {
            output = ColourUtil.colourise("Lucky! ", ColourUtil.white)
                    + output
                    + ColourUtil.colourise(" bonus experience points.", ColourUtil.white);
        } else {
            output += ColourUtil.colourise(" experience points.", ColourUtil.white);
        }

        if (currentXP >= xpToLevel[level + 1]) {
            output += "\n" + levelUp();
        }
        return output;
    }

    public String levelUp() {
        level++;

        String output = ColourUtil.colourise("You have levelled up!", ColourUtil.white);

        maxHealth = maxHealth + 5;
        output += "\nYour health has been increased by 5!";

        maxSpecial = maxSpecial + 5;
        output += "\nYour special has been increased by 5!";


        if (vocation.incAttack()) {
            attack++;
            output += "\nYour attack has been increased by 1!";
        }
        if (vocation.incDefence()) {
            defence++;
            output += "\nYour defence has been increased by 1!";
        }
        if (vocation.incIntelligence()) {
            intelligence++;
            output += "\nYour intelligence has been increased by 1!";
        }
        if (vocation.incSpeed()) {
            speed++;
            output += "\nYour speed has been increased by 1!";
        }
        if (vocation.incLuck()) {
            luck++;
            output += "\nYour luck has been increased by 1!";
        }

        health = maxHealth;
        special = maxSpecial;

        output += vocation.newSkillsLearned(level);

        return ColourUtil.colourise(output, ColourUtil.white);
    }

    @Override
    public void giveCurrencyAmount(int g) {
        this.currencyAmount = this.currencyAmount + g;
    }

    @Override
    public void giveItem(Item i) {
        inventory.add(i);
    }

    // Search for item. If it's there, decrement uses my one. Return true if it
    // gets destroyed. Otherwise, false.
    @Override
    public boolean useItem(int id) {
        for (int c = 0; c < inventory.size(); c++) {
            if (inventory.get(c).getID() == id) {
                boolean crumbles = inventory.get(c).useOnce();
                if (crumbles) {
                    removeInventoryItem(inventory.get(c).getName());
                    return true;
                }
                return false;
            }
        }
        return false;
    }

    @Override
    public void removeInventoryItem(String item) {
        for (int c = 0; c < inventory.size(); c++) {
            if (inventory.get(c).getName().equalsIgnoreCase(item)) {
                Item i = inventory.get(c);
                inventory.remove(c);
            }
        }
    }

    @Override
    public int getWeaponID() {
        return weapon.getID();
    }

    @Override
    public int getOffhandID() {
        return offhand.getID();
    }

    @Override
    public int getHelmID() {
        return helm.getID();
    }

    @Override
    public int getChestID() {
        return chest.getID();
    }

    @Override
    public int getBootsID() {
        return boots.getID();
    }

    @Override
    public Item getInventoryItem(String item) {
        for (int c = 0; c < inventory.size(); c++) {
            if (inventory.get(c).getName().equalsIgnoreCase(item)) {
                Item i = inventory.get(c);
                return i;
            }
        }
        return new ItemImp();
    }

    @Override
    public Item getInventoryItem(int x) {
        if (inventory.isEmpty() || x >= inventory.size()) {
            return (new ItemImp());
        } else {
            Item returnItem = inventory.get(x);
            return returnItem;
        }
    }

    @Override
    public BookEntry[] getAllLogEntries() {
        return ((LogBookEdit) log).getAllEntries();
    }

    @Override
    public String equip(String object) {
        for (int c = 0; c < inventory.size(); c++) {
            if (inventory.get(c).getName().equalsIgnoreCase(object)) {
                // Item found in inventory!
                Item i = inventory.get(c);
                if (i.getItemType().equalsIgnoreCase("WEAPON")) {
                    return equipWeapon(i);
                } else if (i.getItemType().equalsIgnoreCase("OFFHAND")) {
                    return equipOffhand(i);
                } else if (i.getItemType().equalsIgnoreCase("HEAD")) {
                    return equipHelm(i);
                } else if (i.getItemType().equalsIgnoreCase("CHEST")) {
                    return equipChest(i);
                } else if (i.getItemType().equalsIgnoreCase("FEET")) {
                    return equipBoots(i);
                } else {
                    return "You cannot equip "
                            + ColourUtil.colourise(i.getName(), ColourUtil.item) + "!";
                }
            }
        }
        return "You don't have any " + object + "!";
    }

    @Override
    public String equipWeapon(Item i) {
        if (weapon.getID() > 0) {
            inventory.add(weapon);
            String output = "You remove "
                    + ColourUtil.colourise(weapon.getName(), ColourUtil.item) + ".\n";

            weapon = i;
            inventory.remove(i);
            output += "You wield "
                    + ColourUtil.colourise(weapon.getName(), ColourUtil.item) + ".";

            return output;
        } else {
            weapon = i;
            inventory.remove(i);
            String output = "You wield "
                    + ColourUtil.colourise(weapon.getName(), ColourUtil.item) + ".";

            return output;
        }
    }

    @Override
    public String equipOffhand(Item i) {
        if (offhand.getID() > 0) {
            inventory.add(offhand);
            String output = "You remove "
                    + ColourUtil.colourise(offhand.getName(), ColourUtil.item) + ".\n";

            offhand = i;
            inventory.remove(i);
            output += "You hold "
                    + ColourUtil.colourise(offhand.getName(), ColourUtil.item)
                    + " in your off-hand.";

            return output;
        } else {
            offhand = i;
            inventory.remove(i);
            String output = "You hold "
                    + ColourUtil.colourise(offhand.getName(), ColourUtil.item)
                    + " in your off-hand.";

            return output;
        }
    }

    @Override
    public String equipHelm(Item i) {
        if (helm.getID() > 0) {
            inventory.add(helm);
            String output = "You remove "
                    + ColourUtil.colourise(helm.getName(), ColourUtil.item) + ".\n";

            helm = i;
            inventory.remove(i);
            output += "You wear "
                    + ColourUtil.colourise(helm.getName(), ColourUtil.item)
                    + " on your head.";

            return output;
        } else {
            helm = i;
            inventory.remove(i);
            String output = "You wear "
                    + ColourUtil.colourise(helm.getName(), ColourUtil.item)
                    + " on your head.";

            return output;
        }
    }

    @Override
    public String equipChest(Item i) {
        if (chest.getID() > 0) {
            inventory.add(chest);
            String output = "You remove "
                    + ColourUtil.colourise(chest.getName(), ColourUtil.item) + ".\n";

            chest = i;
            inventory.remove(i);
            output += "You wear "
                    + ColourUtil.colourise(chest.getName(), ColourUtil.item) + ".";

            return output;
        } else {
            chest = i;
            inventory.remove(i);
            String output = "You wear "
                    + ColourUtil.colourise(chest.getName(), ColourUtil.item) + ".";

            return output;
        }
    }

    @Override
    public String equipBoots(Item i) {
        if (boots.getID() > 0) {
            inventory.add(boots);
            String output = "You remove "
                    + ColourUtil.colourise(boots.getName(), ColourUtil.item) + ".\n";

            boots = i;
            inventory.remove(i);
            output += "You wear "
                    + ColourUtil.colourise(boots.getName(), ColourUtil.item)
                    + " on your feet.";

            return output;
        } else {
            boots = i;
            inventory.remove(i);
            String output = "You wear "
                    + ColourUtil.colourise(boots.getName(), ColourUtil.item)
                    + " on your feet.";

            return output;
        }
    }

    @Override
    public String remove(String object) {
        if (weapon.getName().equalsIgnoreCase(object)) {
            inventory.add(weapon);
            String weaponName = weapon.getName();
            weapon = new ItemImp();
            return "You remove "
                    + ColourUtil.colourise(weaponName, ColourUtil.item) + ".";
        } else if (offhand.getName().equalsIgnoreCase(object)) {
            inventory.add(offhand);
            String offhandName = offhand.getName();
            offhand = new ItemImp();
            return "You remove "
                    + ColourUtil.colourise(offhandName, ColourUtil.item) + ".";
        } else if (helm.getName().equalsIgnoreCase(object)) {
            inventory.add(helm);
            String helmName = helm.getName();
            helm = new ItemImp();
            return "You remove "
                    + ColourUtil.colourise(helmName, ColourUtil.item) + ".";
        } else if (chest.getName().equalsIgnoreCase(object)) {
            inventory.add(chest);
            String chestName = chest.getName();
            chest = new ItemImp();
            return "You remove "
                    + ColourUtil.colourise(chestName, ColourUtil.item) + ".";
        } else if (boots.getName().equalsIgnoreCase(object)) {
            inventory.add(boots);
            String bootsName = boots.getName();
            boots = new ItemImp();
            return "You remove "
                    + ColourUtil.colourise(bootsName, ColourUtil.item) + ".";
        }

        return "You are not wearing " + object + ".";
    }

    @Override
    public boolean inCombat() {
        if (inCombatWith.size() > 0) {
            return true;
        }
        return false;
    }

    @Override
    public boolean pvpImmune() {
        if (System.currentTimeMillis() < lastDeath + 30000) {
            return true;
        }
        return false;
    }

    // Puts mobile to top of queue of mobiles to attack
    // Returns true if the player if mainly attacking this mobile
    // Pulls aggro by making target attack the attacking player
    @Override
    public boolean attack(Fightable target) {
        if (inCombatWith.size() > 0 && inCombatWith.get(0).equals(target)) {
            return false;
        }

        if (!target.isDead()) {
            inCombatWith.remove((Fightable) target);
            inCombatWith.add(0, (Fightable) target);
            System.out.println("Player " + name + " attacking " + target.getAliveName());
            target.attack((Fightable) this);
            return true;
        }
        return false;
    }

    @Override
    public int takeCurrency(int quant) {
        if (currencyAmount >= quant) {
            currencyAmount -= quant;
            return quant;
        } else {
            int amountTaken = currencyAmount;
            currencyAmount = 0;
            return amountTaken;
        }
    }

    @Override
    public int takeCurrency() {
        int amountTaken = currencyAmount;
        currencyAmount = 0;
        return amountTaken;
    }

    @Override
    public void stopFighting(Fightable victim) {
        inCombatWith.remove(victim);
    }

    @Override
    public void stopFighting() {
        inCombatWith = new ArrayList<Fightable>();
    }

    /*@Override
    public Player[] getLocalPlayerTargets() {
        Player p;
        ArrayList<Player> targets = new ArrayList<Player>();
        for (int c = 0; c < inCombatWith.size(); c++) {
            if (inCombatWith.get(c).isPlayer()) {
                p = (Player) inCombatWith.get(c);
                if (p.x() == x && p.y() == y && p.z() == z) {
                    targets.add(p);
                }
            }
        }

        // Convert to regular array
        Player[] ptargets = new Player[targets.size()];
        for (int c = 0; c < targets.size(); c++) {
            ptargets[c] = targets.get(c);
        }
        return ptargets;
    }

    @Override
    public Mobile[] getLocalMobileTargets() {
        Mobile m;
        ArrayList<Mobile> targets = new ArrayList<Mobile>();

        for (int c = 0; c < inCombatWith.size(); c++) {
            if (inCombatWith.get(c).isPlayer()) {
                m = (Mobile) inCombatWith.get(c);
                if (m.x() == x && m.y() == y && m.z() == z) {
                    targets.add(m);
                }
            }
        }

        // Convert to regular array
        Mobile[] mtargets = new Mobile[targets.size()];
        for (int c = 0; c < targets.size(); c++) {
            mtargets[c] = targets.get(c);
        }
        return mtargets;
    }*/
    
    @Override
    public Fightable[] getLocalTargets() {
        Fightable f;
        ArrayList<Fightable> targetsAR = new ArrayList<Fightable>();
        for (int c = 0; c < inCombatWith.size(); c++) {
                f = inCombatWith.get(c);
                if (f.x() == x && f.y() == y && f.z() == z) {
                    targetsAR.add(f);
                }
            
        }

        // Convert to regular array
        Fightable[] targets = new Fightable[targetsAR.size()];
        for (int c = 0; c < targetsAR.size(); c++) {
            targets[c] = targetsAR.get(c);
        }
        return targets;
    }

    @Override
    public String consider(Fightable target) {
        if (target.isDead()) {
            return "I think you already know who would win that fight.";
        } else {
            int levelDifference = target.getLevel() - level;

            if (target.isPlayer()) {
                return consider(levelDifference, target.getName(), ColourUtil.player);
            } else {
                return consider(levelDifference, target.getName(), ColourUtil.mobile);
            }
        }
    }

    private String consider(int levelDifference, String name, Colour colour) {
        String challenge = "";
        if (levelDifference < -12) {
            challenge = "You would crush "
                    + ColourUtil.colourise(name, colour)
                    + " like a fly.";
        } else if (levelDifference >= -12 && levelDifference <= -9) {
            challenge = ColourUtil.colourise(name, colour)
                    + " is perspiring heavily.";
        } else if (levelDifference >= -8 && levelDifference <= -5) {
            challenge = ColourUtil.colourise(name, colour)
                    + " backs away nervously.";
        } else if (levelDifference >= -4 && levelDifference <= -2) {
            challenge = ColourUtil.colourise(name, colour)
                    + " looks a little worried about the idea.";
        } else if (levelDifference >= -1 && levelDifference <= 1) {
            challenge = ColourUtil.colourise(name, colour)
                    + " should be a fair fight!";
        } else if (levelDifference >= 2 && levelDifference <= 4) {
            challenge = ColourUtil.colourise(name, colour)
                    + " snickers nervously.";
        } else if (levelDifference >= 5 && levelDifference <= 8) {
            challenge = ColourUtil.colourise(name, colour)
                    + " fancies their chances against you!";
        } else if (levelDifference >= 9 && levelDifference <= 12) {
            challenge = ColourUtil.colourise(name, colour)
                    + " turns their nose up at you.";
        } else if (levelDifference > 12) {
            challenge = "Challenging " + ColourUtil.colourise(name, colour)
                    + " would be either very brave, or very stupid.";
        }

        return challenge;
    }

    @Override
    public String consume(String item) {
        boolean found = false;
        for (int c = 0; c < inventory.size() && !found; c++) {
            if (inventory.get(c).getName().equalsIgnoreCase(item)) {
                found = true;
                Item i = inventory.get(c);
                if (i.getItemType().equalsIgnoreCase("FOOD")
                        || i.getItemType().equalsIgnoreCase("HPOTION")) {
                    if (!i.isConsumable() || i.getUsesLeft() > 0) {
                        int preHealth = health;
                        health += i.getModifier();
                        health = Math.min(health, maxHealth);

                        String out = "You consume "
                                + ColourUtil.colourise(i.getName(), ColourUtil.item)
                                + " and recover "
                                + (health - preHealth) + " health.";
                        if (i.useOnce()) {
                            String iname = ColourUtil.colourise(i.getName(), ColourUtil.item);
                            removeInventoryItem(i.getName());
                            return out + "\n" + iname + " crumbles into dust.";
                        } else {
                            return out;
                        }
                    }
                } else if (i.getItemType().equalsIgnoreCase("SPOTION")) {
                    if (!i.isConsumable() || i.getUsesLeft() > 0) {
                        int preSpecial = special;
                        special += i.getModifier();
                        special = Math.min(special, maxSpecial);

                        String out = "You consume "
                                + ColourUtil.colourise(i.getName(), ColourUtil.item)
                                + " and recover "
                                + (special - preSpecial) + " special.";
                        if (i.useOnce()) {
                            return out + "\n"
                                    + ColourUtil.colourise(i.getName(), ColourUtil.item)
                                    + " crumbles into dust.";
                        } else {
                            return out;
                        }
                    }
                }
            }
        }
        if (found) {
            return "You cannot consume that!";
        } else {
            return "You don't have any " + item + ".";
        }
    }

    @Override
    public String compare(String item) {
        boolean found = false;
        for (int c = 0; c < inventory.size() && !found; c++) {
            if (inventory.get(c).getName().equalsIgnoreCase(item)) {
                found = true;
                Item i = inventory.get(c);
                if (i.getItemType().equalsIgnoreCase("WEAPON")) {
                    return compareWeapon(i);
                } else if (i.getItemType().equalsIgnoreCase("OFFHAND")) {
                    return compareOffhand(i);
                } else if (i.getItemType().equalsIgnoreCase("HELM")) {
                    return compareHelm(i);
                } else if (i.getItemType().equalsIgnoreCase("CHEST")) {
                    return compareChest(i);
                } else if (i.getItemType().equalsIgnoreCase("BOOTS")) {
                    return compareBoots(i);
                }
            }
        }
        if (found) {
            return "You're not using anything comparable!";
        } else {
            return "You don't have any " + item + ".";
        }
    }

    private String compareWeapon(Item i) {
        if (i.getModifier() > weapon.getModifier()) {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks better than "
                    + ColourUtil.colourise(weapon.getName(), ColourUtil.item) + ".";
        } else if (i.getModifier() < weapon.getModifier()) {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks worse than "
                    + ColourUtil.colourise(weapon.getName(), ColourUtil.item) + ".";
        } else {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks about the same as "
                    + ColourUtil.colourise(weapon.getName(), ColourUtil.item) + ".";
        }
    }

    private String compareOffhand(Item i) {
        if (i.getModifier() > offhand.getModifier()) {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks better than "
                    + ColourUtil.colourise(offhand.getName(), ColourUtil.item) + ".";
        } else if (i.getModifier() < offhand.getModifier()) {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks worse than "
                    + ColourUtil.colourise(offhand.getName(), ColourUtil.item) + ".";
        } else {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks about the same as "
                    + ColourUtil.colourise(offhand.getName(), ColourUtil.item) + ".";
        }
    }

    private String compareHelm(Item i) {
        if (i.getModifier() > helm.getModifier()) {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks better than "
                    + ColourUtil.colourise(helm.getName(), ColourUtil.item) + ".";
        } else if (i.getModifier() < helm.getModifier()) {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks worse than "
                    + ColourUtil.colourise(helm.getName(), ColourUtil.item) + ".";
        } else {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks about the same as "
                    + ColourUtil.colourise(helm.getName(), ColourUtil.item) + ".";
        }
    }

    private String compareChest(Item i) {
        if (i.getModifier() > chest.getModifier()) {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks better than "
                    + ColourUtil.colourise(chest.getName(), ColourUtil.item) + ".";
        } else if (i.getModifier() < chest.getModifier()) {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks worse than "
                    + ColourUtil.colourise(chest.getName(), ColourUtil.item) + ".";
        } else {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks about the same as "
                    + ColourUtil.colourise(chest.getName(), ColourUtil.item) + ".";
        }

    }

    private String compareBoots(Item i) {
        if (i.getModifier() > boots.getModifier()) {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks better than "
                    + ColourUtil.colourise(boots.getName(), ColourUtil.item) + ".";
        } else if (i.getModifier() < boots.getModifier()) {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks worse than "
                    + ColourUtil.colourise(boots.getName(), ColourUtil.item) + ".";
        } else {
            return ColourUtil.colourise(i.getName(), ColourUtil.item)
                    + " looks about the same as "
                    + ColourUtil.colourise(boots.getName(), ColourUtil.item) + ".";
        }

    }

    @Override
    public boolean updateSpawn(int spawnLevel) {
        if (this.level >= spawnLevel && spawnLevel > 0) {
            this.spawnX = x;
            this.spawnY = y;
            this.spawnZ = z;
            return true;
        }
        return false;
    }

    @Override
    public void respawn() {
        lastDeath = System.currentTimeMillis();
        x = spawnX;
        y = spawnY;
        z = spawnZ;
        health = maxHealth / 5;
        currencyAmount = 0;
        inventory = new ArrayList<Item>();
        weapon = new ItemImp();
        offhand = new ItemImp();
        helm = new ItemImp();
        chest = new ItemImp();
        boots = new ItemImp();
    }

    @Override
    public void recall() {
        x = spawnX;
        y = spawnY;
        z = spawnZ;
        special = special / 2;
    }

    @Override
    public void tickRegen() {
        System.out.println("TickRegen: " + name);
        double modifier = 1;

        if (inCombat()) {
            System.out.println("In combat");
            modifier = 1 / 3;

        } else if (sitting) {
            System.out.println("Sitting");
            modifier = 2;
        }


        health += Math.max(1, (maxHealth * 0.15 + defence * 0.15) * modifier);
        if (health > maxHealth) {
            health = maxHealth;
        }

        System.out.println("Special regen: " + (maxSpecial * 0.15 + intelligence * 0.15) * modifier);

        System.out.println("That's (" + maxSpecial + " x 0.15 + " + intelligence + " x 0.15) x " + modifier);

        special += Math.max(1, (maxSpecial * 0.15 + intelligence * 0.15) * modifier);
        if (special > maxSpecial) {
            special = maxSpecial;
        }
    }

    @Override
    public boolean lightSourceEquipped() {
        if (weapon.isLightSource()) {
            return true;
        } else if (offhand.isLightSource()) {
            return true;
        } else if (helm.isLightSource()) {
            return true;
        } else if (chest.isLightSource()) {
            return true;
        } else if (boots.isLightSource()) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean suppressTick() {
        return suppressTick;
    }

    @Override
    public boolean subscribesTo(String channel) {
        for (int c = 0; c < subscriptions.size(); c++) {
            if (subscriptions.get(c).equalsIgnoreCase(channel)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public String subscribeTo(String channel) {
        if (channel.trim().length() == 0) {
            String out = "Available channels:\n\n";
            out += "Channel  Subscribing\n";
            out += "-------  -----------\n";
            out += "General     " + subscribesTo("general") + "\n";
            out += "OOC         " + subscribesTo("ooc") + "\n";
            out += "Newbie      " + subscribesTo("newbie") + "\n";
            out += "Trade       " + subscribesTo("trade") + "\n";
            return out;
        } else if (!subscribesTo(channel)) {
            if (channel.equalsIgnoreCase("general")
                    || channel.equalsIgnoreCase("ooc")
                    || channel.equalsIgnoreCase("newbie")
                    || channel.equalsIgnoreCase("trade")) {
                subscriptions.add(channel.toUpperCase());
                return "You are now subscribed to " + channel.toLowerCase() + ".";
            } else {
                return channel.toLowerCase() + " is not a valid channel!";
            }
        } else {
            subscriptions.remove(channel.toUpperCase());
            return "You have unsubscribed to " + channel.toLowerCase() + ".";
        }
    }

    public boolean hasCombatFlag(String flag) {
        while (combatFlags.iterator().hasNext()) {
            if (combatFlags.iterator().next().isType(flag)) {
                return true;
            }
        }
        return false;
    }

    public boolean hasCombatFlag(Flag flag) {
        while (combatFlags.iterator().hasNext()) {
            if (combatFlags.iterator().next() == flag) {
                return true;
            }
        }
        return false;
    }

    public void addCombatFlag(Flag flag) {
        if (!hasCombatFlag(flag)) {
            combatFlags.add(flag);
        }
    }

    @Override
    public void setSuppressTick(boolean val) {
        suppressTick = val;
    }

    public void expireCombatFlags() {
        while (combatFlags.iterator().hasNext()) {
            if (combatFlags.iterator().next().expired()) {
                combatFlags.iterator().remove();
            }
        }
    }

    @Override
    public boolean hasLogEntry(int id) {
        return log.entryExists(id);
    }

    @Override
    public void newLogEntry(BookEntry e) {
        log.addEntry(e);
    }

    @Override
    public boolean hasIncompleteLogEntry(int id) {
        return log.entryIncomplete(id);
    }

    @Override
    public boolean completedLogEntry(int id) {
        return log.entryCompleted(id);
    }

    @Override
    public void completeLogEntry(int id) {
        log.completeEntry(id);
    }

    @Override
    public String viewLog(int page) {
        return log.viewLog(page);
    }

    @Override
    public String viewCompleteLog(int page) {
        return log.viewCompleteLog(page);
    }

    @Override
    public String viewLogEntry(int entry) {
        return log.viewEntry(entry);
    }

    @Override
    public boolean loginCorrect(String inpname, String inppass) {
        if (inpname.equals(name) && password.equals(inppass)) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public char getLastMove() {
        return lastMove;
    }

    // This is for other players to call and follow the player
    @Override
    public boolean addToFollowers(Player p) {
        for (int c = 0; c < followers.size(); c++) {
            if (followers.get(c).getID() == p.getID()) {
                return false;
            }
        }
        followers.add(p);
        ((PlayerImp) p).addToFollowing(this);
        return true;
    }

    @Override
    public boolean removeFromFollowers(Player p) {
        for (int c = 0; c < followers.size(); c++) {
            if (followers.get(c).getID() == p.getID()) {
                ((PlayerImp) followers.get(c)).removeFromFollowing(this);
                followers.remove(c);
                return true;
            }
        }
        return false;
    }

    private boolean addToFollowing(Player p) {
        for (int c = 0; c < following.size(); c++) {
            if (following.get(c).getID() == p.getID()) {
                return false;
            }
        }
        following.add(p);
        return true;
    }

    private boolean removeFromFollowing(Player p) {
        for (int c = 0; c < following.size(); c++) {
            if (following.get(c).getID() == p.getID()) {
                following.remove(c);
                return true;
            }
        }
        return false;
    }

    // This checks to see if a specified player is following this
    @Override
    public boolean isFollowing(Player p) {
        if (followers.contains(p) && sameRoom(p)) {
            return true;
        }
        return false;
    }

    @Override
    public Player[] playersFollowing() {
        Player[] out = new Player[following.size()];
        return following.toArray(out);
    }

    @Override
    public Player[] getLocalFollowers() {
        ArrayList<Player> temp = new ArrayList<Player>();
        temp.add(this);
        for (int c = 0; c < followers.size(); c++) {
            if (sameRoom(followers.get(c))) {
                temp.add(followers.get(c));
            }
        }
        Player[] out = new Player[temp.size()];

        return temp.toArray(out);
    }

    // This is for players to add/remove other players into their groups
    @Override
    public boolean addToGroup(Player p) {
        for (int c = 0; c < group.size(); c++) {
            if (group.get(c).getID() == p.getID()) {
                return false;
            }
        }
        group.add(p);
        return true;
    }

    @Override
    public boolean removeFromGroup(Player p) {
        for (int c = 0; c < group.size(); c++) {
            if (group.get(c).getID() == p.getID()) {
                group.remove(c);
                return true;
            }
        }
        return false;
    }

    @Override
    public Player[] playersInGroup() {
        Player[] out = new Player[group.size()];
        out = group.toArray(out);

        return out;
    }

    @Override
    public Player[] getLocalGroup() {
        ArrayList<Player> temp = new ArrayList<Player>();
        temp.add(this);
        for (int c = 0; c < group.size(); c++) {
            if (sameRoom(group.get(c))) {
                temp.add(group.get(c));
            }
        }
        Player[] out = new Player[temp.size()];

        return temp.toArray(out);
    }

    @Override
    public boolean isVocation(int id) {
        return vocation.getID() == id;
    }

    @Override
    public boolean knowsSkill(int id) {
        return vocation.knowsSkill(id, level);
    }

    @Override
    public void learnSkill(Skill s) {
        vocation.learnSkill(s);
    }

    @Override
    public void forgetSkill(Skill s) {
        vocation.forgetSkill(s.getID());
    }

    @Override
    public String performSkill(String[] commandArgs) {
        return vocation.performSkill(this, commandArgs);
    }

    @Override
    public String getSkillInfo(String skillName) {
        return vocation.getSkillInfo(skillName);
    }
}
